/*=============================================================================
PostProcessMisc : misc post processing
Copyright (c) 2001 Crytek Studios. All Rights Reserved.

Revision history:
* 23/02/2005: Re-factored/Converted to CryEngine 2.0 by Tiago Sousa
* Created by Tiago Sousa

=============================================================================*/

#include "StdAfx.h"
#include "DriverD3D.h"
#include "I3DEngine.h"
#include "D3DPostProcess.h"

#pragma warning(disable: 4244)

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void CVolumetricScattering::Render()
{
  PROFILE_SHADER_START

    // quickie-prototype
    //    - some ideas: add several types (cloudy/sparky/yadayada)

    // Get current viewport
    int iTempX, iTempY, iWidth, iHeight;
  gcpRendD3D->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);

  gcpRendD3D->Set2DMode(false, 1, 1);       

  //////////////////////////////////////////////////////////////////////////////////////////////////
  // Render god-rays into low-res render target for less fillrate hit

  gcpRendD3D->FX_PushRenderTarget(0,  CTexture::s_ptexBackBufferScaled[1], NULL); 
  gcpRendD3D->RT_SetViewport(0, 0, CTexture::s_ptexBackBufferScaled[1]->GetWidth(), CTexture::s_ptexBackBufferScaled[1]->GetHeight());        

  ColorF clearColor(0, 0, 0, 0);
  gcpRendD3D->EF_ClearBuffers(FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE, &clearColor);


  float fAmount = m_pAmount->GetParam();
  float fTilling = m_pTilling->GetParam();
  float fSpeed = m_pSpeed->GetParam();
  Vec4 pColor = m_pColor->GetParamVec4();

  static CCryNameTSCRC pTechName("VolumetricScattering");
  uint32 nPasses;
  CShaderMan::m_shPostEffects->FXSetTechnique(pTechName);
  CShaderMan::m_shPostEffects->FXBegin(&nPasses, FEF_DONTSETSTATES);

  gcpRendD3D->SetCullMode( R_CULL_NONE );
  gcpRendD3D->FX_SetState(GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCCOL | GS_NODEPTHTEST);    

  int nSlicesCount = 10;   

  Vec4 pParams;
  pParams= Vec4(fTilling, fSpeed, fTilling, fSpeed);

  static CCryNameR pParam0Name("VolumetricScattering");
  static CCryNameR pParam1Name("VolumetricScatteringColor");

  static CCryNameR pParam2Name("PI_volScatterParamsVS");
  static CCryNameR pParam3Name("PI_volScatterParamsPS");

  for( int r(0); r < nSlicesCount; ++r)   
  {
    // !force updating constants per-pass! (dx10..)
    CShaderMan::m_shPostEffects->FXBeginPass(0);

    // Set PS default params  
    Vec4 pParamsPI = Vec4(1.0f, fAmount, r, 1.0f / (float) nSlicesCount);
    CShaderMan::m_shPostEffects->FXSetVSFloat(pParam0Name, &pParams, 1);
    CShaderMan::m_shPostEffects->FXSetPSFloat(pParam1Name, &pColor, 1);    
    CShaderMan::m_shPostEffects->FXSetVSFloat(pParam2Name, &pParamsPI, 1);
    CShaderMan::m_shPostEffects->FXSetPSFloat(pParam3Name, &pParamsPI, 1);

    PostProcessUtils().DrawFullScreenQuad(CTexture::s_ptexBackBufferScaled[1]->GetWidth(), CTexture::s_ptexBackBufferScaled[1]->GetHeight()); 

    CShaderMan::m_shPostEffects->FXEndPass();
  }


  CShaderMan::m_shPostEffects->FXEnd(); 

  gcpRendD3D->Set2DMode(true, 1, 1);       

  // Restore previous viewport
  gcpRendD3D->FX_PopRenderTarget(0);
  gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);     

  //////////////////////////////////////////////////////////////////////////////////////////////////
  // Display volumetric scattering effect

  CCryNameTSCRC pTechName0("VolumetricScatteringFinal");

  PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffects, pTechName0, FEF_DONTSETSTATES);   
  gcpRendD3D->FX_SetState(GS_NODEPTHTEST);

  PostProcessUtils().DrawFullScreenQuad(CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight()); 
  PostProcessUtils().ShEndPass(); 

  gcpRendD3D->FX_Flush(); 
  PROFILE_SHADER_END  
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void CPost3DRenderer::Render()
{
	PROFILE_LABEL_PUSH( "POST_3D_RENDERER" );
	PROFILE_SHADER_START

	const ColorF clearColor(0.0f, 0.0f, 0.0f, 0.0f);

	// Early out if nothing to render
	SRenderPipeline& RESTRICT_REFERENCE rRP = gcpRendD3D->m_RP;
	uint32 nBatchMask = SRendItem::BatchFlags(EFSLIST_GENERAL, rRP.m_pRLD) 
										| SRendItem::BatchFlags(EFSLIST_TRANSP, rRP.m_pRLD)
										| SRendItem::BatchFlags(EFSLIST_DECAL, rRP.m_pRLD);
	if(!(nBatchMask & FB_POST_3D_RENDER))
	{
		// Clear texture 1st so it won't render black quad
		const uint8 zbufferRenderTile = 2; // Use zbuffer render tile, because in stereo the 1st 2 are used for the eyes
		CTexture::s_ptexBackBufferScaled[0]->SetRenderTargetTile(zbufferRenderTile); 
		gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexBackBufferScaled[0], NULL);
		gcpRendD3D->EF_ClearBuffers(FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE, &clearColor);
		gcpRendD3D->FX_PopRenderTarget(0);
		CTexture::s_ptexBackBufferScaled[0]->SetRenderTargetTile(0);
		return;
	}













	gcpRendD3D->Set2DMode(false, 1, 1);   
	gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexBackBuffer, &gcpRendD3D->m_DepthBufferOrig);

	// Update ambient
	gRenDev->m_cEF.m_PF.post3DRendererAmbient = m_pAmbient->GetParamVec4();

	// Clear render target
	gcpRendD3D->EF_ClearBuffers(FRT_CLEAR_STENCIL|FRT_CLEAR_DEPTH|FRT_CLEAR_IMMEDIATE, &clearColor);

	// Setup stencil
	rRP.m_ForceStateOr |=	GS_STENCIL;

	const int32 nStencilState = STENC_FUNC(FSS_STENCFUNC_ALWAYS) |STENCOP_FAIL(FSS_STENCOP_KEEP) | STENCOP_ZFAIL(FSS_STENCOP_KEEP) | STENCOP_PASS(FSS_STENCOP_REPLACE);
	gcpRendD3D->FX_SetStencilState( nStencilState, 1, 0xFF, 0xFF);

	GetUtils().SetupStencilStates(FSS_STENCFUNC_EQUAL); 

	gcpRendD3D->m_nStencilMaskRef = 1;
	GetUtils().BeginStencilPrePass( true );

	// Create custom camera so FOV is the same when ever we render
	CCamera prevCamera = gcpRendD3D.GetCamera();
	CCamera postRenderCamera = prevCamera;
	float fov = DEFAULT_FOV * clamp_tpl<float>( m_pFOVScale->GetParam(), 0.05f, 1.0f);
	postRenderCamera.SetFrustum(prevCamera.GetViewSurfaceX(),prevCamera.GetViewSurfaceZ(),fov,DEFAULT_NEAR,DEFAULT_FAR,prevCamera.GetPixelAspectRatio());
	gcpRendD3D.SetCamera(postRenderCamera);

	uint32 prevAnd = rRP.m_ForceStateAnd;
	uint32 prevOr = rRP.m_ForceStateOr;
	rRP.m_ForceStateAnd |= GS_DEPTHFUNC_EQUAL;
	rRP.m_ForceStateOr |= GS_DEPTHWRITE;
	rRP.m_PersFlags2 |= RBPF2_POST_3D_RENDERER_PASS;
	I3DEngine* p3DEngine = gEnv->p3DEngine;
	gcpRendD3D->SetFog(0.0f, 0.0f, max(p3DEngine->GetMaxViewDistance(),0.f), &clearColor.r, R_FOGMODE_LINEAR);

	// Draw custom objects
	gcpRendD3D->FX_ProcessPostRenderLists(FB_POST_3D_RENDER);

	// Set everything back again
	rRP.m_ForceStateAnd = prevAnd;
	rRP.m_ForceStateOr = prevOr;
	rRP.m_PersFlags2 &= ~RBPF2_POST_3D_RENDERER_PASS;
	gcpRendD3D.SetCamera(prevCamera);
	p3DEngine->SetupDistanceFog();

	GetUtils().EndStencilPrePass(  );

	gcpRendD3D->FX_PopRenderTarget(0);
	gcpRendD3D->Set2DMode(true, 1, 1);   





	// Alpha correction - Override alpha using stencil, otherwise the alpha from the diffuse map will get copied  
	// into render target, which then will get used when drawing the 3D objects to screen
	CTexture::s_ptexBackBuffer->SetRenderTargetTile(1); 
	gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexBackBuffer, &gcpRendD3D->m_DepthBufferOrig);

	PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffects, m_alphaCorrectionTechName, FEF_DONTSETSTATES);   

	GetUtils().SetupStencilStates(FSS_STENCFUNC_EQUAL); 
	gcpRendD3D->FX_SetState( GS_NODEPTHTEST | GS_STENCIL | GS_BLSRC_ONE | GS_BLDST_ONE);     

	PostProcessUtils().DrawFullScreenQuad(CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight()); 

	PostProcessUtils().ShEndPass();   
	GetUtils().SetupStencilStates(-1);
	
	gcpRendD3D->FX_PopRenderTarget(0);
	CTexture::s_ptexBackBuffer->SetRenderTargetTile(0);

	// Gamma correction and down sample to get 4x multisampling
	CTexture::s_ptexBackBufferScaled[0]->SetRenderTargetTile(1); 
	gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexBackBufferScaled[0], NULL);
	gcpRendD3D->RT_SetViewport(0, 0, CTexture::s_ptexBackBufferScaled[0]->GetWidth(), CTexture::s_ptexBackBufferScaled[0]->GetHeight());
	gcpRendD3D->EF_ClearBuffers(FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE, &clearColor);

	PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffects, m_gammaCorrectionTechName, FEF_DONTSETSTATES | FEF_DONTSETTEXTURES);   

	gcpRendD3D->FX_SetState( GS_NODEPTHTEST | GS_BLSRC_SRCALPHA | GS_BLDST_ZERO );     

	GetUtils().SetTexture(CTexture::s_ptexBackBuffer, 0, FILTER_LINEAR, TADDR_WRAP);
	PostProcessUtils().DrawFullScreenQuad(CTexture::s_ptexBackBufferScaled[0]->GetWidth(), CTexture::s_ptexBackBufferScaled[0]->GetHeight()); 

	PostProcessUtils().ShEndPass();   

	gcpRendD3D->RT_SetViewport(0, 0, CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight());
	gcpRendD3D->FX_PopRenderTarget(0);
	CTexture::s_ptexBackBufferScaled[0]->SetRenderTargetTile(0);



























	PROFILE_SHADER_END
	PROFILE_LABEL_POP( "POST_3D_RENDERER" );
}
